// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/debug/debug-type-profile.h"

#include "src/feedback-vector.h"
#include "src/isolate.h"
#include "src/objects-inl.h"
#include "src/objects.h"

namespace v8 {
namespace internal {

    std::unique_ptr<TypeProfile> TypeProfile::Collect(Isolate* isolate)
    {
        std::unique_ptr<TypeProfile> result(new TypeProfile());

        // Feedback vectors are already listed to prevent losing them to GC.
        DCHECK(isolate->factory()
                   ->feedback_vectors_for_profiling_tools()
                   ->IsArrayList());
        Handle<ArrayList> list = Handle<ArrayList>::cast(
            isolate->factory()->feedback_vectors_for_profiling_tools());

        Script::Iterator scripts(isolate);

        for (Script script = scripts.Next(); !script.is_null();
             script = scripts.Next()) {
            if (!script->IsUserJavaScript()) {
                continue;
            }

            Handle<Script> script_handle(script, isolate);

            TypeProfileScript type_profile_script(script_handle);
            std::vector<TypeProfileEntry>* entries = &type_profile_script.entries;

            // TODO(franzih): Sort the vectors by script first instead of iterating
            // the list multiple times.
            for (int i = 0; i < list->Length(); i++) {
                FeedbackVector vector = FeedbackVector::cast(list->Get(i));
                SharedFunctionInfo info = vector->shared_function_info();
                DCHECK(info->IsSubjectToDebugging());

                // Match vectors with script.
                if (script != info->script()) {
                    continue;
                }
                if (!info->HasFeedbackMetadata() || info->feedback_metadata()->is_empty() || !info->feedback_metadata()->HasTypeProfileSlot()) {
                    continue;
                }
                FeedbackSlot slot = vector->GetTypeProfileSlot();
                FeedbackNexus nexus(vector, slot);
                Handle<String> name(info->DebugName(), isolate);
                std::vector<int> source_positions = nexus.GetSourcePositions();
                for (int position : source_positions) {
                    DCHECK_GE(position, 0);
                    entries->emplace_back(position, nexus.GetTypesForSourcePositions(static_cast<uint32_t>(position)));
                }

                // Releases type profile data collected so far.
                nexus.ResetTypeProfile();
            }
            if (!entries->empty()) {
                result->emplace_back(type_profile_script);
            }
        }
        return result;
    }

    void TypeProfile::SelectMode(Isolate* isolate, debug::TypeProfileMode mode)
    {
        HandleScope handle_scope(isolate);

        if (mode == debug::TypeProfileMode::kNone) {
            if (!isolate->factory()
                     ->feedback_vectors_for_profiling_tools()
                     ->IsUndefined(isolate)) {
                // Release type profile data collected so far.

                // Feedback vectors are already listed to prevent losing them to GC.
                DCHECK(isolate->factory()
                           ->feedback_vectors_for_profiling_tools()
                           ->IsArrayList());
                Handle<ArrayList> list = Handle<ArrayList>::cast(
                    isolate->factory()->feedback_vectors_for_profiling_tools());

                for (int i = 0; i < list->Length(); i++) {
                    FeedbackVector vector = FeedbackVector::cast(list->Get(i));
                    SharedFunctionInfo info = vector->shared_function_info();
                    DCHECK(info->IsSubjectToDebugging());
                    if (info->feedback_metadata()->HasTypeProfileSlot()) {
                        FeedbackSlot slot = vector->GetTypeProfileSlot();
                        FeedbackNexus nexus(vector, slot);
                        nexus.ResetTypeProfile();
                    }
                }

                // Delete the feedback vectors from the list if they're not used by code
                // coverage.
                if (isolate->is_best_effort_code_coverage()) {
                    isolate->SetFeedbackVectorsForProfilingTools(
                        ReadOnlyRoots(isolate).undefined_value());
                }
            }
        } else {
            DCHECK_EQ(debug::TypeProfileMode::kCollect, mode);
            isolate->MaybeInitializeVectorListFromHeap();
        }
        isolate->set_type_profile_mode(mode);
    }

} // namespace internal
} // namespace v8
